package org.jeecg.config.mybatis.handler;

import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;
import com.alibaba.druid.sql.ast.statement.SQLDeleteStatement;
import com.alibaba.druid.sql.ast.statement.SQLInsertStatement;
import com.alibaba.druid.sql.ast.statement.SQLUpdateSetItem;
import com.alibaba.druid.sql.ast.statement.SQLUpdateStatement;
import com.sinoframework.web.servlet.interceptor.mybatis.ParamMapUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.jdbc.SQL;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.jeecg.common.util.oConvertUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.DefaultTypedTuple;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import java.lang.reflect.InvocationTargetException;
import java.sql.PreparedStatement;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @title: UploadFileInfoHandler
 * @Author BaoJiaXin
 * @Date: 2024/1/12 11:10
 */

@Component
public class UploadFileInfoHandler {

    public static final String OSS_FILE = "ossFile";
    @Autowired
    private JdbcTemplate jdbcTemplate;


    //key:表名  val:字段名
    private Map<String,List<String>> tableInfoMap ;

    public UploadFileInfoHandler() {

    }

    public Object insert(Invocation invocation, Object parameter, MappedStatement mappedStatement) throws InvocationTargetException, IllegalAccessException {
        BoundSql boundSql = mappedStatement.getBoundSql(parameter);
        List<ParameterMapping> mappingList = boundSql.getParameterMappings();
        Map<String ,String> columnToProMap = ParamMapUtil.columnToProMap(mappingList);
        String sql = boundSql.getSql();

        SQLStatement sqlStatement = SQLUtils.parseSingleMysqlStatement(sql);
        SQLInsertStatement sqlInsertStatement =  (SQLInsertStatement)sqlStatement;
        String table = sqlInsertStatement.getTableName().getSimpleName();
        String camelTable = oConvertUtils.camelName(table);
        Object proceedObject = null;
        //如果table不错在返回
        if (!tableInfoMap.containsKey(camelTable)){
            return  invocation.proceed();
        }


        MetaObject paraMeta = SystemMetaObject.forObject(parameter);
        List<SQLExpr> insertItems = sqlInsertStatement.getColumns();
        List<String> columns = tableInfoMap.get(camelTable);
        List<String> removeList = new ArrayList<>();
        String item = null;
        for (SQLExpr insertItem : insertItems) {
            item = oConvertUtils.camelName(insertItem.toString());
            Mono.just(item) //更新的字段 A
                    .filter(columnName->columns.contains(columnName))//A 否在配置文件中
                    .map(columnName->{
                        Object s= paraMeta.getValue(columnToProMap.get(columnName));
                        return s;
                    })//读取字段 A值
                    .filter(Objects::nonNull)//A值 不是null
                    .map(String::valueOf)//A值 转换成字符串
                    .filter(StringUtils::isNotBlank)//A值 不是空字符
                    .map(columnVal->Arrays.asList(columnVal.split(",")))
                    .subscribe(columnValList->{
                        removeList.addAll(columnValList);
                    });
        }

        //1.执行业务逻辑
        proceedObject = invocation.proceed();
        //2.执行redis文件上传逻辑
        // 删除redis中预删除的文件信息
        if (removeList.size()>0){
            redisTemplate.opsForZSet().remove(OSS_FILE,removeList.toArray());
            log.info("删除redis文件列表信息 zSetKey:{} zSetVal:{}",OSS_FILE,removeList);
        }

        return proceedObject;
    }


    public Object update(Invocation invocation, Object parameter, MappedStatement mappedStatement) throws InvocationTargetException, IllegalAccessException {
        BoundSql boundSql = mappedStatement.getBoundSql(parameter);
        List<ParameterMapping> mappingList = boundSql.getParameterMappings();
        Map<String ,String> columnToProMap = ParamMapUtil.columnToProMap(mappingList);
        String sql = boundSql.getSql();


        SQLStatement sqlStatement = SQLUtils.parseSingleMysqlStatement(sql);
        SQLUpdateStatement sqlUpdateStatement =  (SQLUpdateStatement)sqlStatement;
        String table = sqlUpdateStatement.getTableName().getSimpleName();
        String camelTable = oConvertUtils.camelName(table);
        Object proceedObject = null;
        //如果table不错在返回
        if (!tableInfoMap.containsKey(camelTable)){
            return  invocation.proceed();
        }

        //获取配置文件中表名对应的文件字段列名
        List<String> columns = tableInfoMap.get(camelTable);
        MetaObject paraMeta = SystemMetaObject.forObject(parameter);
        SQL sqlSelect = new SQL();
        sqlSelect.FROM(table);
        //解析更新字段
        Map<String,List<String>> newUrlMap = new HashMap<>();
        List<SQLUpdateSetItem> setItems = sqlUpdateStatement.getItems();
        for (SQLUpdateSetItem setItem : setItems) {
            SQLExpr columnExpr = setItem.getColumn();
            if (columnExpr instanceof SQLIdentifierExpr) {
//                SQLExpr value = setItem.getValue();
                String column = ((SQLIdentifierExpr) columnExpr).getName();
                String camelColumn = oConvertUtils.camelName(column);
                //找出需要处理的字段
                if (columns.contains(camelColumn)){
                    Mono.just(camelColumn)
                            .map(cName->paraMeta.getValue(columnToProMap.get(cName)))
                            .filter(Objects::nonNull)
                            .map(String::valueOf)
                            .filter(StringUtils::isNotBlank)//A值 不是空字符
                            .map(cVal->Arrays.asList(cVal.split(",")))
                            .subscribe(cValList->{
                                sqlSelect.SELECT(column);
                                newUrlMap.put(camelColumn,cValList);
                            });
                }
            }else{
                log.warn("当前 columnExpr 不能解析:{}",columnExpr.getClass().getName());
            }
        }
        if (newUrlMap.size()==0){
            return invocation.proceed();
        }


        String sqlStr = sqlSelect.WHERE(sqlUpdateStatement.getWhere().toString()).toString();
        List<Map<String,List<String>>> rowList= new ArrayList<>();
        //查询要更新字段的值
        List<ParameterMapping> finalMappingList = mappingList.subList(setItems.size(),mappingList.size());
        this.jdbcTemplate.query(sqlStr,
                new PreparedStatementSetter() {
                    @Override
                    public void setValues(PreparedStatement ps) throws SQLException {
                        for (int i = 0; i< finalMappingList.size(); i++){
                            Object val = paraMeta.getValue(finalMappingList.get(i).getProperty());
                            log.info("{}  {}",i,val);
                            ps.setObject(i+1,val);
                        }
                    }
                },
                rs -> {
                    ResultSetMetaData mt = rs.getMetaData();
                    Map<String,List<String>> row = new HashMap<>();
                    for (int i = 0; i < mt.getColumnCount(); i++) {
                        String columnName = mt.getColumnName(i + 1);
                        String columnVal = rs.getString(i + 1);
                        if (StringUtils.isNotBlank(columnVal)){
                            row.put(oConvertUtils.camelName(columnName),Arrays.asList(columnVal.split(",")));
                        }
                    }
                    rowList.add(row);

                });
        //					新值(1, 2, 3, 4, 5);
        //					旧值(3, 4, 5, 6, 7);
        //					新值与旧值差集为：[1, 2] 需要从redis预删除列表中删除
        //					旧值与新值差集为：[6, 7] 添加到redis预备删除
        //1.执行业务逻辑
        proceedObject = invocation.proceed();
        //2.执行redis文件上传逻辑
        //执行redis命令
        rowList.stream().forEach(oldRow->{
            //处理单行数据
            Iterator<Map.Entry<String, List<String>>> iterNewUrl = newUrlMap.entrySet().iterator();
            Map.Entry<String, List<String>> entryNewUrl = null;
            while (iterNewUrl.hasNext()){
                entryNewUrl = iterNewUrl.next();

                List<String> oldList = Optional.ofNullable(oldRow.get(entryNewUrl.getKey())).orElse(new ArrayList<>());

                Collection<String> subtract1 =  CollectionUtils.subtract(entryNewUrl.getValue(),oldList);
                Collection<DefaultTypedTuple<String>> subtract2 = CollectionUtils.subtract(oldList,entryNewUrl.getValue())
                        .stream().map(el->{
                            DefaultTypedTuple<String> typedTuple = new DefaultTypedTuple<>(el,Double.valueOf(System.currentTimeMillis()));
                            return typedTuple;
                        })
                        .collect(Collectors.toList());

                //删除redis中预删除的文件信息
                if (!CollectionUtils.isEmpty(subtract1)){
                    redisTemplate.opsForZSet().remove(OSS_FILE,subtract1);
                    log.info("删除redis文件列表信息 zSetKey:{} zSetVal:{}",OSS_FILE,subtract1);
                }
                if (!CollectionUtils.isEmpty(subtract2)){
                    //添加到redis预备删除
                    redisTemplate.opsForZSet().add(OSS_FILE,new TreeSet<>(subtract2));
                    log.info("添加到redis文件列表信息 zSetKey:{} zSetVal:{}",OSS_FILE,subtract2);
                }
            }
        });

        return proceedObject;

    }



    public Object delete(Invocation invocation, Object parameter, MappedStatement mappedStatement) throws InvocationTargetException, IllegalAccessException {
        BoundSql boundSql = mappedStatement.getBoundSql(parameter);
        List<ParameterMapping> mappingList = boundSql.getParameterMappings();
        Map<String ,String> columnToProMap = ParamMapUtil.columnToProMap(mappingList);
        String sql = boundSql.getSql();


        SQLStatement sqlStatement = SQLUtils.parseSingleMysqlStatement(sql);
        SQLDeleteStatement sqlDeleteStatement =  (SQLDeleteStatement)sqlStatement;
        String table = sqlDeleteStatement.getTableName().getSimpleName();
        String camelTable = oConvertUtils.camelName(table);
        Object proceedObject = null;
        //获取配置文件中表名对应的文件字段列名
        List<String> columns = tableInfoMap.get(camelTable);

        //如果table不错在返回
        if (!tableInfoMap.containsKey(camelTable)){
            return  invocation.proceed();
        }

        SQL sqlSelect = new SQL();
        sqlSelect.FROM(table);
        tableInfoMap.get(camelTable).stream().forEach(c->{
            sqlSelect.SELECT(oConvertUtils.camelToUnderline(c));
        });

        MetaObject paraMeta = SystemMetaObject.forObject(parameter);

        String sqlstr = sqlSelect.WHERE(sqlDeleteStatement.getWhere().toString()).toString();

        List<Map<String,List<String>>> rowList= new ArrayList<>();
        List<ParameterMapping> finalMappingList = mappingList;
        //查询要删除的字段的值
        this.jdbcTemplate.query(sqlstr,
                new PreparedStatementSetter() {
                    @Override
                    public void setValues(PreparedStatement ps) throws SQLException {
                        for (int i = 0; i< finalMappingList.size(); i++){
                            Object val = paraMeta.getValue(finalMappingList.get(i).getProperty());
                            log.info("{}  {}",i,val);
                            ps.setObject(i+1,val);
                        }
                    }
                },
                rs -> {
                    ResultSetMetaData mt = rs.getMetaData();
                    Map<String,List<String>> row = new HashMap<>();
                    for (int i = 0; i < mt.getColumnCount(); i++) {
                        String columnName = mt.getColumnName(i + 1);
                        String columnVal = rs.getString(i + 1);
                        if (StringUtils.isNotBlank(columnVal)){
                            row.put(oConvertUtils.camelName(columnName),Arrays.asList(columnVal.split(",")));
                        }
                    }
                    rowList.add(row);
                });

        //1.执行业务逻辑
        proceedObject = invocation.proceed();
        //2.执行redis文件上传逻辑
        rowList.stream().forEach(row->{
            //处理单行数据
            Iterator<Map.Entry<String, List<String>>> iter = row.entrySet().iterator();
            Map.Entry<String, List<String>> olderEntry = null;
            //循环单行的每个字段
            while (iter.hasNext()){
                olderEntry = iter.next();
                Collection<DefaultTypedTuple<String>> delList = olderEntry.getValue()
                        .stream()
                        .filter(StringUtils::isNotBlank)
                        .map(el->{
                            DefaultTypedTuple<String> typedTuple = new DefaultTypedTuple<>(el,Double.valueOf(System.currentTimeMillis()));
                            return typedTuple;
                        })
                        .collect(Collectors.toList());

                //添加到redis预备删除
                if (delList.size()>0){
                    //添加到redis预备删除
                    redisTemplate.opsForZSet().add(OSS_FILE,new TreeSet<>(delList));
                    log.info("添加到redis文件列表信息 zSetKey:{} zSetVal:{}",OSS_FILE,delList);
                }

            }
        });
        return proceedObject;

    }


}
